/**
 * =====================================================================================
 *       @file  ngx_palloc.c
 *      @brief  
 *
 *  Detailed description starts here.
 *
 *   @internal
 *     Created  2012年05月08日 22时32分20秒 
 *    Revision  1.0.0.0
 *    Compiler  gcc/g++
 *
 *     @author  luis
 * =====================================================================================
 */

#include <stdlib.h>
#include "ngx_palloc.h"
#include "ngx_alloc.h"
#include "ngx_string.h"
#include "ngx_config.h"

void *ngx_alloc(size_t size)
{
    void *p;
    
    p = malloc(size); 
    if (p == NULL)
    {
        perror("malloc failed"); 
    }

    return p;
}

void *ngx_calloc(size_t size)
{
    void *p;
    
    p = ngx_alloc(size); 
    if (p)
    {
        ngx_memzero(p, size);
    }

    return p;
}

ngx_pool_t *ngx_create_pool(size_t size)
{
    ngx_pool_t *p; 

    p = ngx_memalign(NGX_POOL_ALIGNMENT, size);
    if (p == NULL)
    {
        perror("ngx memalign failed");
        return NULL;
    }

    p->d.last = (u_char *)p + sizeof(ngx_pool_t);
    p->d.end = (u_char *)p + size;
    p->d.next = NULL;
    p->d.failed = 0;

    size = size - sizeof(ngx_pool_t); 
    p->max = (size < NGX_MAX_ALLOC_FROM_POOL) ? size : NGX_MAX_ALLOC_FROM_POOL;

    p->current = p;
    p->chain = NULL;
    p->large = NULL;
    p->cleanup = NULL;

    return p;
}

void ngx_destroy_pool(ngx_pool_t *pool)
{
    ngx_pool_t          *p, *n; 
    ngx_pool_large_t    *l;
    ngx_pool_cleanup_t  *c;

    for (c = pool->cleanup; c; c = c->next)
    {
        if (c->handler)
        {
            c->handler(c->data);
        }
    }

    for (l = pool->large; l; l = l->next)
    {
        if (l->alloc)
        {
            ngx_free(l->alloc);
        }
    }

    for (p = pool, n = pool->d.next; ; p = n, n = n->d.next)
    {
        ngx_free(p);

        if (n == NULL)
        {
            break;
        }
    }    
}

static void *ngx_palloc_block(ngx_pool_t *pool, size_t size)
{
    u_char      *m;
    size_t      psize;
    ngx_pool_t  *p, *new, *current;

    psize = (size_t)(pool->d.end - (u_char *)pool);    

    m = ngx_memalign(NGX_POOL_ALIGNMENT, psize);
    if (m == NULL)
    {
        return NULL;
    }
    
    new = (ngx_pool_t *)m;
    
    new->d.end = m + psize;
    new->d.next = NULL;
    new->d.failed = 0;

    m += sizeof(ngx_pool_data_t);
    m = ngx_align_ptr(m, NGX_ALIGNMENT);
    new->d.last = m + size;

    current = pool->current;
    
    for (p = current; p->d.next; p = p->d.next)
    {
        if (p->d.failed++ > 4)  
        {
            current = p->d.next;
        }
    }

    p->d.next = new; 

    pool->current = current ? current : new;

    return m;
}

static void *ngx_palloc_large(ngx_pool_t *pool, size_t size)
{
    void                *p; 
    ngx_uint_t           n;
    ngx_pool_large_t    *large;
    
    p = ngx_alloc(size);
    if (p == NULL)
    {
        return NULL;
    }

    n = 0;
    
    for (large = pool->large; large; large = large->next)
    {
        if (large->alloc == NULL)
        {
            large->alloc = p;
            return p;
        }
    
        if (n++ > 3)
        {
            break;
        }
    }

    large = ngx_palloc(pool, sizeof(ngx_pool_large_t));
    if (large == NULL)
    {
        ngx_free(p); 
        return NULL;
    }

    large->alloc = p;
    large->next = pool->large;
    pool->large = large;

    return p;
}

void *ngx_palloc(ngx_pool_t *pool, size_t size)
{
    u_char      *m;  
    ngx_pool_t  *p;

    if (size <= pool->max)
    {
        p = pool->current; 

        do
        {
            m = ngx_align_ptr(p->d.last, NGX_ALIGNMENT);        

            if (((size_t)(p->d.end - m)) >= size)
            {
                p->d.last = m + size;
                
                return m;
            }            

            p = p->d.next;
        } while (p);

        return ngx_palloc_block(pool, size);
    }    

    return ngx_palloc_large(pool, size);
}

void *ngx_pcalloc(ngx_pool_t *pool, size_t size)
{
    void *p; 

    p = ngx_palloc(pool, size);
    if (p)
    {
        ngx_memzero(p, size);
    }

    return p;
}
